/**
 * 
 */
package dh.abs.info;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import dh.abs.Environment;
import dh.abs.SuspendException;
import dh.abs.Suspendable;
import dh.abs.util.SimulationRandom;

/**
 * This class represents the environment of information diffusion simulation
 * @author denialhumanity
 *
 */
public class InformationDiffusionEnvironment implements Environment,Suspendable {
	/** current step number */
	private int currentStep;
	
	/** max step number. when current step reaches this step, simulation is stopped. */
	private int maxStep;
	
	/** list of agents. */
	private List<InformationDiffusionAgent> agentList;
	
	/** map of id-agent */
	private Map<String, InformationDiffusionAgent> agentMap;
	
	/**
	 * Default constructor. All the parameters are initialized by default values.
	 */
	public InformationDiffusionEnvironment() {
		init();
	}
	
	/**
	 * This method initializes all the field of this instance, and must be called in constructor.
	 */
	private void init() {
		agentList = new ArrayList<InformationDiffusionAgent>();
		agentMap = new HashMap<String, InformationDiffusionAgent>();
	}

	/* (non-Javadoc)
	 * @see dh.abs.Environment#preStep()
	 */
	public void preStep() {
		for(InformationDiffusionAgent agent: agentList) {
			agent.preStep();
		}
	}

	/* (non-Javadoc)
	 * @see dh.abs.Environment#nextStep()
	 */
	public void nextStep() {
		InformationDiffusionAgent agent = agentList.get(SimulationRandom.getInstance().getRand().nextInt(agentList.size()));
		agent.nextStep();
	}

	/* (non-Javadoc)
	 * @see dh.abs.Environment#postStep()
	 */
	public void postStep() {
		for(InformationDiffusionAgent agent: agentList) {
			agent.postStep();
		}
		currentStep++;
	}

	
	/**
	 * Add one agent to this simulation.
	 * @param agent
	 */
	public void addAgent(InformationDiffusionAgent agent) {
		agentList.add(agent);
		agentMap.put(agent.getID(), agent);
	}
	
	/**
	 * add agent collection to this simulation.
	 * @param agentColelcetion
	 */
	public void addAgentCollection(Collection<InformationDiffusionAgent> agentColelcetion) {
		for(InformationDiffusionAgent a: agentColelcetion) {
			addAgent(a);
		}
	}
	/**
	 * @return the currentStep
	 */
	public int getCurrentStep() {
		return currentStep;
	}

	/**
	 * @param currentStep the currentStep to set
	 */
	public void setCurrentStep(int currentStep) {
		this.currentStep = currentStep;
	}

	/**
	 * @return the maxStep
	 */
	public int getMaxStep() {
		return maxStep;
	}

	/**
	 * @param maxStep the maxStep to set
	 */
	public void setMaxStep(int maxStep) {
		this.maxStep = maxStep;
	}

	/**
	 * @return the agentList
	 */
	public List<InformationDiffusionAgent> getAgentList() {
		return agentList;
	}
	

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		
		sb.append("step:" + currentStep);
		sb.append("\r\n");
		
		for(InformationDiffusionAgent agent: agentList) {
			for(Information info: agent.getInfoList()) {
				sb.append(info.toString());
				sb.append("\r\n");
			}
		}
		
		return sb.toString();
	}

	/* (non-Javadoc)
	 * @see dh.abs.Suspendable#getID()
	 */
	public String getID() {
		return String.valueOf(hashCode());
	}
	
	/**
	 * @param id
	 * @return
	 */
	public InformationDiffusionAgent getAgentByID(String id) {
		return agentMap.get(id);
	}

	/* (non-Javadoc)
	 * @see dh.abs.Suspendable#storeToFile(java.io.File)
	 */
	public void storeToFile(File file) throws SuspendException {
		String prefix = file.getParent();
		file.getParentFile().mkdirs();
		
		for(InformationDiffusionAgent agent: agentList) {
			File agentFile = new File(prefix + File.separator + InformationDiffusionAgent.class.getSimpleName() + agent.getID());
			agent.storeToFile(agentFile);
		}
		
	}

	/* (non-Javadoc)
	 * @see dh.abs.Suspendable#loadFromFile(java.io.File)
	 */
	public void loadFromFile(File file) throws SuspendException {
		//nothing to do
	}

	/**
	 * calculate influence as a final result.
	 * This method is expected to be called when this simulation is finished.
	 * @return
	 */
	public Map<InformationDiffusionAgent, Double> calcInfluence() {
		Map<InformationDiffusionAgent, Double> inflMap = new HashMap<InformationDiffusionAgent, Double>();
		
		for(InformationDiffusionAgent agent: agentList) {
			for(Information info: agent.getInfoList()) {
				Map<InformationDiffusionAgent, Double> tmpMap = info.getInfluence();
				
				for(InformationDiffusionAgent a: tmpMap.keySet()) {
					Double t = 0.0;
					if(inflMap.containsKey(a)) {
						t = inflMap.remove(a);
					}
					inflMap.put(a, t + tmpMap.get(a));
				}
			}
		}
		
		return inflMap;
	}
	
	/**
	 * This method outputs the calculated influence to the file.
	 * @param file
	 */
	public void outputInfluence(File file) {
		BufferedWriter bw = null;
		
		try {
			bw = new BufferedWriter(new FileWriter(file));
			Map<InformationDiffusionAgent, Double> map = calcInfluence();
			for(InformationDiffusionAgent a: map.keySet()) {
				bw.write(a.getID() + " " + map.get(a));
				bw.newLine();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(bw != null) {
				try {
					bw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
